package com.cmdi.service.impl;

import com.cmdi.domain.MinIoUploadResDTO;
import io.minio.MinioClient;
import io.minio.PutObjectOptions;
import io.minio.Result;
import io.minio.errors.*;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

import java.io.InputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * @Description: Minio文件操作实现类
 * @Author: gaozb
 */
@Slf4j
@Component
public class MinioClientOperationImpl {

    @Autowired
    public MinioClient minioClient;

    private static final String PATH_SEPARATOR = "/";
    private static final String SEPARATOR_DOT = ".";

    /**
     * @param bucketName: 桶名称
     * @Description: 检查桶是否存在
     **/
    public boolean bucketExists(String bucketName) {
        try {
            return minioClient.bucketExists(bucketName);
        } catch (Exception e) {
            log.error("错误原因", e);
        }
        return false;
    }

    /**
     * @param bucketName: 桶名称
     * @Description: 创建桶
     **/
    public void makeBucket(String bucketName) {
        try {
            boolean isExist = minioClient.bucketExists(bucketName);
            if (!isExist) {
                minioClient.makeBucket(bucketName);
            }
        } catch (Exception e) {
            log.error("错误原因", e);
        }
    }

    /**
     * @param bucketName: 桶名称
     * @Description: 列出存储桶中的所有对象
     **/
    public Iterable<Result<Item>> listObjects(String bucketName) {
        boolean flag = bucketExists(bucketName);
        if (flag) {
            try {
                Iterable<Result<Item>> results = minioClient.listObjects(bucketName);
                return results;
            } catch (XmlParserException e) {
                log.error("错误原因", e);
            }
        }
        return null;
    }

    /**
     * @param bucketName: 桶名称
     * @Description: 列出存储桶中的目录
     **/
    public List<String> listDirs(String bucketName, String prefix) {
        boolean flag = bucketExists(bucketName);
        ArrayList<String> dirs = new ArrayList<>();
        if (flag) {
            try {
                Iterable<Result<Item>> results = minioClient.listObjects(bucketName, prefix, false);
                for (Result<Item> result : results) {
                    if(result.get().isDir()) {
                        dirs.add(result.get().objectName());
                    }
                }
                return dirs;
            } catch (Exception e) {
                log.error("错误原因", e);
            }
        }
        return null;
    }

    /**
     * @param bucketName: 桶名称
     * @Description: 删除桶
     **/
    public boolean removeBucket(String bucketName) {
        boolean flag = bucketExists(bucketName);
        if (flag) {
            Iterable<Result<Item>> myObjects = listObjects(bucketName);
            for (Result<Item> result : myObjects) {
                Item item = null;
                try {
                    item = result.get();
                    // 有对象文件，则删除失败
                    if (item.size() > 0) {
                        return false;
                    }
                } catch (Exception e) {
                    log.error("错误原因", e);
                }

            }
            // 删除存储桶，只有存储桶为空时才能删除成功。
            try {
                minioClient.removeBucket(bucketName);
            } catch (Exception e) {
                log.error("错误原因", e);
            }
            flag = bucketExists(bucketName);
            if (!flag) {
                return true;
            }
        }
        return false;
    }

    /**
     * @Description: 获取文件存储服务的所有存储桶名称
     **/
    public List<String> listBucketNames() {
        List<Bucket> bucketList = listBuckets();
        List<String> bucketListName = bucketList.stream().map(bucket -> bucket.name()).collect(Collectors.toList());
        return bucketListName;
    }

    /**
     * @Description 列出所有存储桶
     **/
    public List<Bucket> listBuckets() {
        try {
            return minioClient.listBuckets();
        } catch (Exception e) {
            log.error("错误原因", e);
        }
        return null;
    }

    /**
     * @Description 判断文件是否存在
     * @param bucketName: 桶名称
     * @param objectName: 文件名称
     **/
    public boolean objectExists(String bucketName, String objectName) {
        boolean flag = bucketExists(bucketName);
        if(flag) {
            try {
                Iterable<Result<Item>> results = minioClient.listObjects(bucketName, objectName);
                for (Result<Item> result : results) {
                    if(objectName.equals(result.get().objectName())) {
                        return true;
                    }
                }
            } catch (Exception e) {
                log.error("错误原因", e);
            }
        }

        return false;
    }

    /**
     * @Description 保留文件名的上传
     * @param bucketName: 桶名称
     * @param path: 上传到哪个目录
     * @param multipartFile: 文件
     **/
    public MinIoUploadResDTO uploadOrignFileName(String bucketName, String path, MultipartFile multipartFile) throws Exception {
        if(!this.bucketExists(bucketName)){
            this.makeBucket(bucketName);
        }
        InputStream in = multipartFile.getInputStream();
        // PutObjectOptions，上传配置(文件大小，内存中文件分片大小)
        PutObjectOptions putObjectOptions = new PutObjectOptions(in.available(), -1);
        // 文件的ContentType
        putObjectOptions.setContentType(multipartFile.getContentType());
        String objectFullPath = path + PATH_SEPARATOR + multipartFile.getOriginalFilename();
        minioClient.putObject(bucketName, objectFullPath, in,putObjectOptions);

        //minioClient.presignedGetObject(bucketName, objectFullPath, 1)
        // 返回生成文件名、访问路径
        return new MinIoUploadResDTO(objectFullPath,
                multipartFile.getOriginalFilename(),
               null);
    }

    /**
     * @Description 设置文件分享
     * @param bucketName: 桶名称
     * @param objectFullPath: 文件全路径
     * @param ex: 文件分享过期时间
     **/
    public MinIoUploadResDTO setShareTime(String bucketName, String objectFullPath, Integer ex) throws Exception {
        // 返回生成文件名、访问路径

        return new MinIoUploadResDTO(objectFullPath,
                objectFullPath.substring(objectFullPath.lastIndexOf(PATH_SEPARATOR) + 1),
                minioClient.presignedGetObject(bucketName, objectFullPath, ex));
    }

    /**
     * @Description 文件上传
     * @param bucketName: 桶名称
     * @param path: 上传到哪个目录
     * @param multipartFile: 文件
     **/
    public MinIoUploadResDTO uploadNewFileName(String bucketName, String path, MultipartFile multipartFile) throws Exception {
        // bucket 不存在，创建
        if (!this.bucketExists(bucketName)) {
            this.makeBucket(bucketName);
        }
        InputStream inputStream = multipartFile.getInputStream();
        // PutObjectOptions，上传配置(文件大小，内存中文件分片大小)
        PutObjectOptions putObjectOptions = new PutObjectOptions(inputStream.available(), -1);
        // 文件的ContentType
        putObjectOptions.setContentType(multipartFile.getContentType());
        String fileName = minFileName(multipartFile.getOriginalFilename());
        String objectFullPath = path + PATH_SEPARATOR + fileName;
        minioClient.putObject(bucketName, objectFullPath, inputStream, putObjectOptions);
        //minioClient.presignedGetObject(bucketName, objectFullPath, 1)
        // 返回生成文件名、访问路径
        return new MinIoUploadResDTO(objectFullPath,
                fileName,
                null);
    }

    /**
     * @Description 文件上传
     * @param bucketName: 桶名称
     * @param fileName: 文件全路径
     **/
    public void download(HttpServletResponse response, String bucketName, String fileName) throws Exception {
        InputStream fileInputStream = minioClient.getObject(bucketName, fileName);
        String dF = fileName.substring(fileName.lastIndexOf(PATH_SEPARATOR) + 1);
        String downName = URLEncoder.encode(dF, "UTF-8");
        //response.setContentType("application/force-download");
        //response.setContentType("application/octet-stream");
        response.setHeader("Content-Type", "application/octet-stream;charset=utf-8");
        response.setHeader("Content-Disposition", "attachment;filename=" + downName + ";filename*=utf-8''" + downName);
        response.setCharacterEncoding("UTF-8");
        ServletOutputStream out = response.getOutputStream();
        try {
            IOUtils.copy(fileInputStream,out);
        } catch (Exception e) {
            System.out.println(e);
        } finally {
            fileInputStream.close();
            out.close();
        }
    }

    /**
     * @Description: 文件删除
     * @param bucketName: 桶名称
     * @param fileName: 文件全路径
     */
    public boolean removeFile(String bucketName, String fileName) {
        boolean flag = bucketExists(bucketName);
        if (flag) {
            try {
                minioClient.removeObject(bucketName, fileName);
                return true;
            } catch (Exception e) {
                log.info("错误原因", e);
            }
        }
        return false;
    }

    /**
     * @Description: 生成新的文件名
     * @param originalFileName: 原文件名
     */
    private String minFileName(String originalFileName) {
        //得到文件类型
        String suffix  = originalFileName;
        if(originalFileName.contains(SEPARATOR_DOT)) {
            suffix = originalFileName.substring(originalFileName.lastIndexOf(SEPARATOR_DOT));
        }
        //随机产生字符串+文件类型
        return UUID.randomUUID().toString().toUpperCase()+suffix;
    }

}